import webpack, { BannerPlugin, Configuration, ContextReplacementPlugin } from 'webpack';
import path from 'path';
import HtmlWebpackPlugin from 'html-webpack-plugin';
import HtmlWebpackTagsPlugin from 'html-webpack-tags-plugin';
import { CleanWebpackPlugin } from 'clean-webpack-plugin';
import { WebpackManifestPlugin } from 'webpack-manifest-plugin';
import TerserWebpackPlugin from 'terser-webpack-plugin';
import MiniExtractWebpackPlugin from 'mini-css-extract-plugin';
// import CopyWebpackPlugin from 'copy-webpack-plugin';
import CaseSensitivePathsPlugin from 'case-sensitive-paths-webpack-plugin';
import WebpackBar from 'webpackbar';
import LicenseCheckerWebpackPlugin from 'license-checker-webpack-plugin';
import ESLintWebpackPlugin from 'eslint-webpack-plugin';
import { resolve } from './helpers';
import getBabelConfig from './getBabelConfig';
import { getOutputDir, getWxConfig } from './config';
import webpackMerge from 'webpack-merge';
// eslint-disable-next-line @typescript-eslint/no-var-requires
const FilterWarningsPlugin = require('webpack-filter-warnings-plugin');

// const smp = new SpeedMeasureWebpackPlugin();
function getProjectPath(...args: string[]) {
  return path.resolve(process.cwd(), ...args);
}

export interface GetWebpackConfigOptions {
  cwd: string;
  mode: 'development' | 'production';
  minimize?: boolean;
  publicPath: string;
}

function normalizeOptions(options?: Partial<GetWebpackConfigOptions>): GetWebpackConfigOptions {
  return {
    cwd: process.cwd(),
    mode: 'production',
    publicPath: '/',
    ...options,
  };
}

export default function (options?: Partial<GetWebpackConfigOptions>): Configuration {
  const normalizedOptions = normalizeOptions(options);
  const { cwd, mode: packMode, minimize: packMinimize , publicPath} = normalizedOptions;
  const distPath = getOutputDir(cwd);
  const isDev = packMode === 'development';
  const mode = isDev ? 'development' : 'production';
  const devtool = isDev ? 'source-map' : false;
  const minimize = normalizedOptions.hasOwnProperty('minimize') ? packMinimize : !isDev;
  const wxConfig = getWxConfig();
  const modifyVars = wxConfig.modifyVars || {};
  const configWrapper = wxConfig.webpack || ((a: any) => a);
  const htmlTitle = wxConfig.inject.title || 'Wx API Docs';
  const htmlIcon = wxConfig.inject.favicon || getProjectPath('docs/assets/favicon.ico');
  const injectScripts = wxConfig.injectScripts || [];
  const injectStyles = wxConfig.injectStyles || [];
  const injectData = wxConfig.injectData || {};
  const projectBabelConfig = getBabelConfig(false);
  const MiniExtractLoader = {
    loader: MiniExtractWebpackPlugin.loader,
    options: {
      publicPath,
      hmr: isDev,
    },
  };
  const config: webpack.Configuration = {
    entry: {
      index: getProjectPath('docs/index.ts'),
    },
    output: {
      path: distPath,
      filename: 'static/js/[name].[hash].js',
      publicPath,
      libraryTarget: 'umd',
      chunkFilename: 'static/js/[id].[contenthash].js',
      umdNamedDefine: true,
      globalObject: 'window',
    },
    mode,
    resolve: {
      extensions: ['js', 'jsx', '.ts', '.tsx', '.js', '.json'],
      alias: {
        '@': getProjectPath('src'),
      },
    },
    module: {
      rules: [
        {
          test: /\.tsx?$/,
          use: [
            {
              loader: resolve('babel-loader'),
              options: projectBabelConfig,
            },
          ],
          exclude: /node_modules/,
        },
        {
          test: /\.jsx?$/,
          use: [
            {
              loader: resolve('babel-loader'),
              options: projectBabelConfig,
            },
          ],
          exclude: /node_modules/,
        },
        {
          test: /\.d\.ts$/,
          loader: resolve('ignore-loader'),
        },
        { test: /\.txt/, use: ['raw-loader'] },
        {
          test: /\.json$/,
          type: 'javascript/auto',
          loader: resolve('json-loader'),
        },
        {
          test: /\.css$/,
          use: [
            MiniExtractLoader,
            {
              loader: resolve('css-loader'),
              options: {
                modules: false,
              },
            },
            {
              loader: resolve('postcss-loader'),
              options: {
                // eslint-disable-next-line @typescript-eslint/no-var-requires
                plugins: () => [require('autoprefixer')()],
              },
            },
          ],
        },
        {
          test: /\.less$/,
          use: [
            MiniExtractLoader,
            {
              loader: resolve('css-loader'),
              options: {
                modules: false,
              },
            },
            {
              loader: resolve('postcss-loader'),
              options: {
                // eslint-disable-next-line @typescript-eslint/no-var-requires
                plugins: () => [require('autoprefixer')()],
              },
            },
            {
              loader: 'less-loader',
              options: {
                javascriptEnabled: true,
                modifyVars,
              },
            },
          ],
        },
        {
          test: /\.scss$/,
          use: [
            MiniExtractLoader,
            {
              loader: 'css-loader',
              options: {
                modules: false,
              },
            },
            {
              loader: 'sass-loader',
            },
          ],
        },
        {
          test: /\.styl$/,
          use: [
            MiniExtractLoader,
            {
              loader: 'css-loader',
              options: {
                modules: false,
              },
            },
            {
              loader: 'stylus-loader',
            },
          ],
        },
        {
          test: /\.(png|jpg|gif|eot|ttf|woff|woff2)$/,
          loader: 'url-loader',
          exclude: /node_modules/,
          options: {
            limit: 15360,
            name: 'assets/[hash].[name].[ext]',
            fallback: 'file-loader',
          },
        },
        {
          test: /\.vx.svg/,
          use: {
            loader: 'svg-sprite-loader',
            options: {},
          },
        },
      ],
    },
    devtool,
    externals: {
      'react-dom': 'ReactDOM',
      react: 'React',
    },
    plugins: [
      new ContextReplacementPlugin(/moment[\\\/]locale$/, /^\.\/(zh-cn)$/),
      new CaseSensitivePathsPlugin(),
      new CleanWebpackPlugin({
        verbose: false,
        dry: false,
      }),
      new HtmlWebpackPlugin({
        filename: 'index.html',
        template: getProjectPath('docs/assets/index.html'),
        title: htmlTitle,
        favicon: htmlIcon,
        injectScripts,
        injectStyles,
        injectData,
        minify: minimize,
      }),
      new HtmlWebpackTagsPlugin({
        scripts: [].map(() => 'dll.js'),
        hash: true,
        publicPath: path.posix.join('/', '/static/js'),
        append: false,
      }),
      new FilterWarningsPlugin({
        // suppress conflicting order warnings from mini-css-extract-plugin.
        // ref: https://github.com/ant-design/ant-design/issues/14895
        // see https://github.com/webpack-contrib/mini-css-extract-plugin/issues/250
        exclude: /mini-css-extract-plugin[^]*Conflicting order/,
      }),
      new MiniExtractWebpackPlugin({
        filename: 'static/css/[name].[contenthash].css',
        chunkFilename: 'static/css/[id].[contenthash].css',
      }),
      new WebpackManifestPlugin(),
      new BannerPlugin({
        banner: `
Copyright 2017-present, 微风平台, Inc.
All rights reserved.
      `, // 其值为字符串，将作为注释存在
        raw: false, // 如果值为 true，将直出，不会被作为注释
        entryOnly: true, // 如果值为 true，将只在入口 chunks 文件中添加
      }),
      new WebpackBar({
        name: 'Wx Tools',
        color: '#0d878e',
        profile: true,
        basic: false,
      }),
      new ESLintWebpackPlugin({}),
    ],
    optimization: {
      minimize,
      moduleIds: 'hashed',
      minimizer: [
        // eslint-disable-next-line @typescript-eslint/ban-ts-comment
        // @ts-ignore
        new TerserWebpackPlugin({
          parallel: true, // 开启多进程编译,大大提高构建速度
          extractComments: false,
          // sourceMap: true, // 开启sourceMap, 将错误信息映射到相应模块(会减慢编译速度)
          terserOptions: {
            // 设置压缩js的选项,对比之前的配置,只需要使用默认选项即可
            // warnings: false, // 在UglifyJs删除没有用到的代码时不输出警告
            compress: {
              collapse_vars: true,
              drop_console: false, // 删除所有的 `console` 语句，可以兼容ie浏览器
              reduce_vars: true, // 提取出出现多次但是没有定义成变量去引用的静态值
            },
            output: {
              comments: false, // 剔除所有注释(默认)
              beautify: false, // 最紧凑的输出(默认)
            },
          },
        }),
      ],
      runtimeChunk: {
        // 单独生成一个chunk, 包含了运行时每个入口点
        name: (entrypoint: any) => `runtimes~${entrypoint.name}`,
      },
      splitChunks: {
        chunks: 'all',
        minSize: 30000,
        minChunks: 3,
        // 分割代码
        cacheGroups: {
          // 缓存组
          node_modules: {
            name: 'vendor', // 组的名字
            test: /[\\/]node_modules[\\/]/, // 正则匹配规则
            chunks: 'all', // 'initial', 'async', 'all' 分别对应优化时只选择非异步引入，异步引入, 所有引入方式
            minChunks: 2, // 触发分割代码优化的最小公用量, 2就比较合适啦~
            priority: -10, // 触发优先级
          },
        },
      },
    },
  };
  // eslint-disable-next-line @typescript-eslint/ban-ts-comment
  //@ts-ignore
  // return smp.wrap(config);
  const mergeConfig = webpackMerge(config, {});
  return configWrapper(mergeConfig) || mergeConfig;
}
